package service

import (
	"encoding/json"
	"fmt"
	"gitee.com/daka1004/devops-runner/common/logs"
	"gitee.com/daka1004/devops-runner/dto"
	"gitee.com/daka1004/devops-runner/models"
	"gitee.com/daka1004/devops-runner/modules/mq"
	"gitee.com/daka1004/devops-runner/modules/setting"
	"github.com/gorilla/websocket"
	"golang.org/x/text/encoding/simplifiedchinese"
	"golang.org/x/text/transform"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

type RunnerService struct{}

func (r RunnerService) DeleteNodeLog(req dto.RunnerPipeline) (int64, interface{}, error) {
	//TODO implement me
	workspace := filepath.Join("workspaces", (req.ExecuteID))
	logName := fmt.Sprintf("run_%s", req.NodeUuid)
	getwd, err := os.Getwd()
	logPath := filepath.Join(getwd, workspace, logName)
	err = os.Remove(logPath)
	if err != nil {
		return 0, nil, err
	}
	return 0, nil, nil
	//panic("implement me")
	panic("implement me")
}

func (r RunnerService) GetNodeLog(req dto.RunnerPipeline) (int64, interface{}, error) {
	//TODO implement me
	workspace := filepath.Join("workspaces", (req.ExecuteID))
	logName := fmt.Sprintf("run_%s", req.NodeUuid)
	getwd, err := os.Getwd()
	logPath := filepath.Join(getwd, workspace, logName)
	logData, err := os.ReadFile(logPath)
	if err != nil {
		return 0, nil, err
	}
	return 0, string(logData), nil
	//panic("implement me")
}

type ConsoleAndLogWriter struct {
	logFile *os.File
	mu      sync.Mutex
}

func (w *ConsoleAndLogWriter) Write(p []byte) (n int, err error) {
	w.mu.Lock()
	defer w.mu.Unlock()
	// 先在控制台打印
	fmt.Print(string(p))
	// 再写入日志文件
	return w.logFile.Write(p)
}
func (r RunnerService) Execute(req dto.RunnerPipeline) (int64, interface{}, error) {
	url := fmt.Sprintf("ws://%s:%d/ssh/agentLogger?nodeUUID=%s&executeLoggerUUID=%s", setting.Setting.ServerIP, 8280, req.NodeUuid, req.ExecuteID)
	//url := fmt.Sprintf("ws://%s:%d/ssh/agentLogger?executingId=%s", setting.Setting.ServerIP, 8280, req.ExecuteID)
	//url := fmt.Sprintf("ws://%s:%d/ssh/logger?nodeUUID=%s&executeLoggerUUID=%s", setting.Setting.ServerIP, 8280, req.NodeUuid, req.ExecuteID)
	//manager, err := mq.NewWebSocketManager(url)
	logs.Infof("url is : %v\n", url)
	//func executePipeline(w http.ResponseWriter, r *http.Request) {
	//time.Sleep(5 * time.Second)
	task := &models.Task{
		PipelineID: req.PipelineID,
		ExecuteID:  req.ExecuteID,
		Command:    req.Command,
		Status:     models.StatusRunning,
		Serial:     req.Serial, //默认不能重复
	}
	logs.Info("--------------task------------", task, task.ExecuteID)
	task.Workspace = createWorkspace(task.ExecuteID)
	logName := fmt.Sprintf("run_%s", req.NodeUuid)
	getwd, err := os.Getwd()
	task.LogPath = filepath.Join(getwd, task.Workspace, logName)
	task.Status = models.StatusRunning
	//}
	logs.Info("logpath     ", task.LogPath)
	logs.Info("workspace     ", task.Workspace)
	// 创建用于停止日志读取的通道
	// 创建临时脚本文件
	//getwd, err := os.Getwd()
	absWorkPath := filepath.Join(getwd, task.Workspace)
	scriptPath, err := createScriptFile(absWorkPath, task.Command, req.NodeUuid)
	if err != nil {
		handleError(task, err)
		return 0, nil, err
	}
	logs.Info(scriptPath)
	//defer os.Remove(scriptPath) // 执行后删除脚本
	// 启动日志读取协程
	logFile, err := os.Create(task.LogPath)
	if err != nil {
		handleError(task, err)
		return 0, nil, err
	}

	cmd, err := prepareCommand(scriptPath)
	cmd.Dir = (absWorkPath)
	if err != nil {
		handleError(task, err)
		return 0, nil, err
	}
	//ws://175.6.27.252:8280/ssh/pipeline?1925443336202776577
	//ws://175.6.27.252:8280/ssh/logger?nodeUUID=node-34d475502f0b11f0828d6d1254b65c7a&executeLoggerUUID=1925443336202776577
	// 创建WebSocket日志记录器
	wsLogger := mq.NewWebSocketLogger(url)
	//defer wsLogger.Close() // 确保最终关闭连接

	// 启动WebSocket连接
	if err := wsLogger.Start(); err != nil {
		logFile.WriteString(fmt.Sprintf("启动WebSocket连接失败: %v\n", err))
		handleError(task, err)
		return 0, nil, err
	}
	var mu sync.Mutex
	if os.PathSeparator == '\\' {
		// Windows系统：实时转换GBK到UTF - 8
		stdout, err := cmd.StdoutPipe()
		if err != nil {
			logFile.WriteString(fmt.Sprintf("获取标准输出管道失败: %v\n", err))
			return 0, nil, err
		}

		stderr, err := cmd.StderrPipe()
		if err != nil {
			logFile.WriteString(fmt.Sprintf("获取标准错误管道失败: %v\n", err))
			return 0, nil, err
		}
		// 启动命令
		if err := cmd.Start(); err != nil {
			logFile.WriteString(fmt.Sprintf("启动命令失败: %v\n", err))
			handleError(task, err)
			return 0, nil, err
		}
		// 异步处理标准输出（实时转换并推送）
		go func() {
			decoder := simplifiedchinese.GBK.NewDecoder()
			reader := transform.NewReader(stdout, decoder)
			buffer := make([]byte, 4096)
			for {
				n, err := reader.Read(buffer)
				if n > 0 {
					data := buffer[:n]
					logContent := string(data)

					mu.Lock()
					_, err = logFile.Write(data)
					mu.Unlock()
					if err != nil {
						logs.Errorf("写入标准输出到日志文件失败: %v\n", err)
					}
					// 使用WebSocketLogger发送日志
					if err := wsLogger.Send(logContent, "log"); err != nil {
						logs.Errorf("发送标准输出日志到WebSocket失败: %v\n", err)
					}
					//err = sendLogToWebSocket(url, logContent, "log")
					//err = sendLogToWebSocket(fmt.Sprintf("ws://%s:%d/ssh/agentLogger?executingId=%s", setting.Setting.ServerIP, 8280, req.ExecuteID), logContent, "stdout", req.ExecuteID)
					//if err != nil {
					//	logs.Errorf("发送标准输出日志到WebSocket失败: %v\n", err)
					//}
				}
				if err != nil {
					if err != io.EOF {
						logs.Errorf("处理标准输出失败: %v\n", err)
					}
					return
				}
			}

		}()

		// 异步处理标准错误（实时转换并推送）
		go func() {
			decoder := simplifiedchinese.GBK.NewDecoder()
			reader := transform.NewReader(stderr, decoder)
			buffer := make([]byte, 4096)
			for {
				n, err := reader.Read(buffer)
				if n > 0 {
					data := buffer[:n]
					logContent := string(data)

					mu.Lock()
					_, err = logFile.Write(data)
					mu.Unlock()
					if err != nil {
						logs.Errorf("写入标准错误到日志文件失败: %v\n", err)
					}
					// 使用WebSocketLogger发送日志
					if err := wsLogger.Send(logContent, "log"); err != nil {
						logs.Errorf("发送标准输出日志到WebSocket失败: %v\n", err)
					}
					//err = sendLogToWebSocket(url, logContent, "log")
					//err = manager.SendMessage(logContent, "log")
					//err = sendLogToWebSocket(fmt.Sprintf("ws://%s:%d/ssh/agentLogger?executingId=%s", setting.Setting.ServerIP, 8280, req.ExecuteID), logContent, "stderr", req.ExecuteID)
					//if err != nil {
					//	logs.Errorf("发送标准错误日志到WebSocket失败: %v\n", err)
					//}
				}
				if err != nil {
					if err != io.EOF {
						logs.Errorf("处理标准错误失败: %v\n", err)
					}
					return
				}
			}
		}()
	} else {
		// Linux/Mac系统：直接使用MultiWriter并推送
		// 创建管道用于实时处理标准输出和标准错误
		stdoutR, stdoutW := io.Pipe()
		stderrR, stderrW := io.Pipe()
		// 将命令的标准输出和标准错误分别连接到管道的写入端
		cmd.Stdout = stdoutW
		cmd.Stderr = stderrW
		// 启动命令
		if err := cmd.Start(); err != nil {
			handleError(task, err)
			return 0, nil, err
		}
		// 异步处理标准输出
		go func() {
			defer stdoutW.Close() // 确保在处理完成后关闭写入端
			// 创建一个MultiWriter，将输出同时写入日志文件和标准输出管道
			//writer := io.MultiWriter(logFile, stdoutW)
			buffer := make([]byte, 4096)
			for {
				n, err := stdoutR.Read(buffer)
				if n > 0 {
					// 将数据写入日志文件
					_, writeErr := logFile.Write(buffer[:n])
					if writeErr != nil {
						logs.Errorf("写入标准输出到日志文件失败: %v\n", writeErr)
					}

					logContent := string(buffer[:n])
					// 使用WebSocketLogger发送日志
					if err := wsLogger.Send(logContent, "log"); err != nil {
						logs.Errorf("发送标准输出日志到WebSocket失败: %v\n", err)
					}
					//err = sendLogToWebSocket(url, logContent, "log")
					//err = manager.SendMessage(logContent, "log")
					//if err != nil {
					//	logs.Errorf("发送标准输出日志到WebSocket失败: %v\n", err)
					//}
				}
				if err != nil {
					if err != io.EOF {
						logs.Errorf("读取标准输出失败: %v\n", err)
					}
					break
				}
			}
			logs.Info("标准输出处理协程完成")
		}()
		// 异步处理标准错误
		go func() {
			defer stderrW.Close() // 确保在处理完成后关闭写入端
			buffer := make([]byte, 4096)
			for {
				n, err := stderrR.Read(buffer)
				if n > 0 {
					// 将数据写入日志文件
					_, writeErr := logFile.Write(buffer[:n])
					if writeErr != nil {
						logs.Errorf("写入标准错误到日志文件失败: %v\n", writeErr)
					}

					logContent := string(buffer[:n])
					// 使用WebSocketLogger发送日志
					if err := wsLogger.Send(logContent, "log"); err != nil {
						logs.Errorf("发送标准输出日志到WebSocket失败: %v\n", err)
					}
					//err = sendLogToWebSocket(url, logContent, "log")
					//err = manager.SendMessage(logContent, "log")
					//if err != nil {
					//	logs.Errorf("发送标准错误日志到WebSocket失败: %v\n", err)
					//}
				}
				if err != nil {
					if err != io.EOF {
						logs.Errorf("读取标准错误失败: %v\n", err)
					}
					break
				}
			}
			logs.Info("标准错误处理协程完成")
		}()
	}
	// 异步等待命令完成
	//time.Sleep(2 * time.Second)
	go func() {
		// 使用defer确保无论函数如何退出，完成消息都会被发送
		defer func() {
			// 恢复可能的panic
			if r := recover(); r != nil {
				logs.Errorf("执行过程中发生panic: %v", r)
				err = fmt.Errorf("执行过程中发生panic: %v", r)
			}

			// 发送执行完成消息，无论成功还是失败
			var statusMsg string
			if err != nil {
				statusMsg = fmt.Sprintf("执行失败: %v", err)
			} else {
				statusMsg = "执行成功完成"
			}

			// 尝试发送完成消息，最多重试3次
			maxRetries := 3
			for i := 0; i < maxRetries; i++ {
				if err := wsLogger.Send(statusMsg, "completion"); err != nil {
					logs.Errorf("第 %d 次尝试发送完成消息失败: %v", i+1, err)

					// 如果连接已关闭，尝试重新连接
					if strings.Contains(err.Error(), "连接已关闭") {
						logs.Info("尝试重新连接WebSocket...")
						if reconnectErr := wsLogger.Start(); reconnectErr != nil {
							logs.Errorf("重新连接失败: %v", reconnectErr)
							time.Sleep(time.Second) // 等待1秒再重试
							continue
						}
					} else {
						time.Sleep(time.Second) // 等待1秒再重试
					}
				} else {
					logs.Info("成功发送完成消息")
					break
				}
			}

			// 延迟关闭日志文件和WebSocket连接
			defer logFile.Close()
			defer wsLogger.Close()
		}()
		err = cmd.Wait()
		if err != nil {
			fmt.Println("执行完成,命令执行出错")
			//logs.Info("执行完成,命令执行出错")
			logs.Errorf("命令执行出错: %v\n", err)

		} else {
			fmt.Println("执行完成,completion")
			//logs.Info("执行完成,completion")
			logs.Info("命令执行成功完成")
		}
		// 发送执行完成消息，通过阻塞的方法，这个标志应该就没有用
		// 使用WebSocketLogger发送日志
		/*if err := wsLogger.Send("执行完成", "completion"); err != nil {
			logs.Errorf("发送标准输出日志到WebSocket失败: %v\n", err)
		}*/
		//err = sendLogToWebSocket(url, "执行完成", "completion")
		//fmt.Println("执行完成,completion")
		//logs.Info("执行完成,completion")
		//err = manager.SendMessage("执行完成", "completion")
		//if err != nil {
		//	logs.Errorf("发送日志文件内容到WebSocket失败: %v\n", err)
		//}
		//defer wsLogger.Close() // 确保最终关闭连接
		//defer logFile.Close()  //文件关闭
	}()
	// err ignore

	//go func() {
	//	err := cmd.Wait() // 这将阻塞直到脚本及其所有子进程完成
	//
	//	if err != nil {
	//		fmt.Printf("命令执行出错: %v\n", err)
	//		// 可以在这里处理错误状态
	//	} else {
	//		fmt.Println("命令执行成功完成")
	//	}
	//
	//	// 无论成功或失败，都关闭停止通道
	//close(stopChan)

	logData, err := os.ReadFile(task.LogPath)

	return 0, string(logData), nil
}

func createWorkspace(taskID string) string {
	/*虽然同一个任务数据隔离可以解决，但是同一个任务会调用多次，并且后面执行依赖前一次的结果，这种情况，如何修改代码？
	**任务保存到数据库一下，同时目录和保存的流水线记录有关！*/
	//当前流水线id,查询数据，如果存在记录则不新建workspace 同一个流水线执行多次的问题 随机数拼接拆分为 taskid携带
	workspace := filepath.Join("workspaces", (taskID))
	os.MkdirAll(workspace, 0755)
	logs.Info("workspace: ", workspace)
	return workspace
}

func handleError(task *models.Task, err error) {
	task.Status = models.StatusFailure
	task.Error = err.Error()
	//db.UpdateTask(task)
	logs.Info("task--------", task.Workspace)
	//sendCallback(task)
}

// LogMessage 定义消息结构体
type LogMessage struct {
	Type    string `json:"type"`
	Content string `json:"content"`
}

// sendLogToWebSocket 函数用于将日志内容通过 WebSocket 发送到服务端
func sendLogToWebSocket(url, logLine string, msgType string) error {
	msg := LogMessage{
		Type:    msgType,
		Content: logLine,
	}
	jsonData, err := json.Marshal(msg)
	if err != nil {
		return fmt.Errorf("序列化日志消息失败: %v", err)
	}
	//url := fmt.Sprintf("ws://%s:%s/ssh/agentLogger?executingId=%s", ip, port, executingId)
	// 尝试连接到 WebSocket 服务端
	fmt.Println("尝试连接到 WebSocket 服务端")
	conn, _, err := websocket.DefaultDialer.Dial(url, nil)
	if err != nil {
		return fmt.Errorf("连接WebSocket服务器失败: %v", err)
	}
	// 确保在函数结束时关闭连接
	defer conn.Close()

	// 发送日志行到 WebSocket 服务器
	//logs.Info(" 发送日志行到 WebSocket 服务器%v", url)
	err = conn.WriteMessage(websocket.TextMessage, jsonData)
	if err != nil {
		return fmt.Errorf("发送日志到 WebSocket 服务器失败: %v\n", err)
	}
	return nil
}

// createScriptFile 根据命令内容创建对应的脚本文件
func createScriptFile(absWorkPath, command, nodeUuid string) (string, error) {
	// 确定脚本文件名和路径

	// 检查 workspace 路径是否存在
	if _, err := os.Stat(absWorkPath); os.IsNotExist(err) {
		return "", fmt.Errorf("指定的工作区路径不存在: %s", absWorkPath)
	}
	scriptName := fmt.Sprintf("script_%s", nodeUuid)
	//scriptName := "script"
	if setting.Setting.IsWindows {
		scriptName += ".bat"
	} else {
		scriptName += ".sh"
	}
	scriptPath := filepath.Join(absWorkPath, scriptName)

	// 根据操作系统创建不同类型的脚本
	var scriptContent string
	if setting.Setting.IsWindows {
		// Windows批处理脚本
		//cd /d "%s"
		scriptContent = fmt.Sprintf(`@echo off
setlocal
%s
`, command)
	} else {
		// Linux shell脚本
		//cd "%s"
		scriptContent = fmt.Sprintf(`#!/bin/bash
%s
`, command)
	}

	// 写入脚本内容
	err := ioutil.WriteFile(scriptPath, []byte(scriptContent), 0755)
	if err != nil {
		return "", fmt.Errorf("创建脚本文件失败: %v", err)
	}

	logs.Errorf("创建脚本文件: %s\n内容:\n%s\n", scriptPath, scriptContent)
	return scriptPath, nil
}
func prepareCommand(scriptPath string) (*exec.Cmd, error) {
	// 检查文件是否存在
	if _, err := os.Stat(scriptPath); os.IsNotExist(err) {
		return nil, fmt.Errorf("脚本文件不存在: %s", scriptPath)
	}

	var cmd *exec.Cmd
	if setting.Setting.IsWindows {
		// 直接传递路径，exec.Command 会自动处理引号
		cmd = exec.Command("cmd", "/C", scriptPath)

		// 打印调试信息
		logs.Errorf("准备执行 Windows 命令: %v\n", cmd.Args)
		logs.Errorf("脚本路径: %s\n", scriptPath)

		// 验证工作目录

		workDir := filepath.Dir(scriptPath)
		if _, err := os.Stat(workDir); os.IsNotExist(err) {
			return nil, fmt.Errorf("工作目录不存在: %s", workDir)
		}
	} else {
		cmd = exec.Command("bash", scriptPath)
		logs.Errorf("准备执行 Linux 命令: %v\n", cmd.Args)
	}

	// 设置工作目录
	//cmd.Dir = filepath.Dir(scriptPath)
	// 设置环境变量
	//cmd.Dir, _ = os.Getwd()

	cmd.Env = os.Environ()
	cmd.Env = append(cmd.Env, "DEVOPS_RUNNER=true")

	return cmd, nil
}

func NewIRunnerService() IRunnerService {
	return &RunnerService{}
}

type IRunnerService interface {
	Execute(req dto.RunnerPipeline) (int64, interface{}, error)
	GetNodeLog(req dto.RunnerPipeline) (int64, interface{}, error)
	DeleteNodeLog(req dto.RunnerPipeline) (int64, interface{}, error)
}
